# -*- coding: utf-8 -*-
import sys
import json
import random
import paramiko
import time
from importlib import reload
import  os
import socket
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtGui import QIcon
from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, QThread, QTimer
from PySide6.QtWidgets import (QFormLayout, QFrame, QHBoxLayout, QLabel,
                               QLineEdit, QPushButton, QScrollArea, QTextEdit,  
                               QWidget, QVBoxLayout, QToolButton)

import ui_main_layout as main_window
import scan_parsing_json
# pip install paramiko
 
def AddTimer(self, time, fun):
    timer=QTimer(self)
    timer.timeout.connect(fun)
    timer.setSingleShot(True)
    timer.start(time)

def AddRepeatedTimer(fun, ):
    pass
# 请求命令工具   


class MyWidget(QtWidgets.QWidget):
    
    def __init__(self  ):
        super().__init__()

        self.ui = main_window.Ui_apollo_command_tools()#实例化UI对象
        self.ui.setupUi(self)#初始化

        self.init()

    # 初始化ui变量
    def init(self):
        self.search_string = "" # 搜索字符串
        self.auto_linefeed = True # 自动换行
        self.displayed_command_list = [ ]  # 显示的命令表

        self.selected_command = 0

        # self.ui.textEdit.document().setMaximumBlockCount(1000) # 设置最大行数
        # self.ui.textEdit.verticalScrollBar().valueChanged.connect(self.ScrollBarScrollEvents)
        self.lineWrapMode()


        self.ui.command_handle_scroll_area.setHidden(True)  
        self.ui.output_log.setHidden(False) 

        self.engine_request = { } # 
        self.all_request = { }
        self.displayed_request = { }


        self.current_project = { } #当前项目
        self.ssh_connect = None

        AddTimer(self, 20, self.ReadingProjectConfiguration)
        # self.ReadingProjectConfiguration()

    # 自动换行
    def lineWrapMode(self):
        huanhmos = QtWidgets.QTextEdit.LineWrapMode.NoWrap  
        if self.auto_linefeed:
            huanhmos = QtWidgets.QTextEdit.LineWrapMode.WidgetWidth # 自动换行
        self.ui.command_description.setLineWrapMode(huanhmos)
        self.ui.parameter_input.setLineWrapMode(huanhmos)
        self.ui.parameter_description.setLineWrapMode(huanhmos)
        self.ui.response_description.setLineWrapMode(huanhmos)

    # 读取项目配置
    def ReadingProjectConfiguration(self, file_path = "星际配置.json"):
        project = scan_parsing_json.reading_project_configuration(file_path)
        print ("project", project)
        if project:
            self.current_project = project

            text = "读取项目配置成功"
            self.ui.output_log.append(text)

            text = "服务器id：{}".format(project["masterip"])
            self.ui.output_log.append(text)

            self.all_request = { }

            # self.ScanEngineCommand()
            # self.ScanModCommand()
            # self.CreateCommandTableButton()
            # self.ConnectToServer()

            AddTimer(self, 50, self.ScanModCommand)
            AddTimer(self, 100, self.ScanEngineCommand)
            AddTimer(self, 150, self.CreateCommandTableButton)
            AddTimer(self, 200, self.ConnectToServer)

    # 扫描引擎命令
    def ScanEngineCommand(self):

        engine_request_file = None
        project = self.current_project
        apollo_doc = project.get("apollo_doc", "engineRequest.json")
        if os.path.exists(apollo_doc):
            engine_request_file = apollo_doc
        elif os.path.exists("engineRequest.json"):
            engine_request_file = "engineRequest.json"

        self.engine_request = None
        # request = parse_json("engineRequest.json")
        if engine_request_file:
            total_number, requests_dict = scan_parsing_json.parse_request_command(engine_request_file)
            # with open("engineRequest.json",   encoding="UTF-8" ) as file:
            #     self.engine_request = json.load(file)
            if requests_dict:
                self.engine_request = requests_dict
                self.all_request.update(requests_dict)
            text = "引擎 总请求数量{}个，成功导入{}个".format(total_number, len(requests_dict))
            self.ui.output_log.append(text)
        else:
            text = "引擎请求命令扫描失败"
            text ='<font color="#BF1C27"> {}</font>'.format(text)
            self.ui.output_log.append(text)

    # 扫描mod命令
    def ScanModCommand(self):
        mod_request_list = [ ]
        project = self.current_project
        for dirpath, dirnames, filenames in os.walk(project["mod_path"]):  
            for file in filenames : 
                if file.lower() == "modRequest.json".lower():
                    fullPath = os.path.join(dirpath, file)
                    mod_request_list.append(fullPath)
                    print (fullPath)

        for file_path in mod_request_list:
            total_number, requests_dict = scan_parsing_json.parse_request_command(file_path)
            text = "mod 总请求数量{}个，成功导入{}个".format(total_number, len(requests_dict))
            self.ui.output_log.append(text)
            if requests_dict:
                self.engine_request = requests_dict
                self.all_request.update(requests_dict)

    # 连接服务器
    def ConnectToServer(self):
        project = self.current_project
        print ("开始链接")
        if not os.path.exists(project["private_key_file"]):
            text = "秘钥文件不存在, 无法连接"
            text ='<font color="#BF1C27"> {}</font>'.format(text)
            self.ui.output_log.append(text)
            return
        private_key = paramiko.RSAKey.from_private_key_file(
            project["private_key_file"], project.get("private_key_password"))

        # 建立一个sshclient对象
        ssh_connect = paramiko.SSHClient()
        # 允许将信任的主机自动加入到 host_allow 列表，此方法必须放在connect方法的前面
        ssh_connect.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        # 调用connect方法连接服务器
        
        try:
            ssh_connect.connect(
                hostname = project["masterip"]     , 
                port     = project.get("username_port", 32200), 
                username = project.get("username", "fuzhu")   , 
                pkey     = private_key,
                timeout = 1
                )
            self.ssh_connect = ssh_connect
            text ='<font color="#32C12A"> {}</font>'.format("服务器连接成功")
            self.ui.output_log.append(text)

        except socket.timeout:
            text = "连接超时"
            self.ui.output_log.append(text)

        except :
            text = "连接错误"
            self.ui.output_log.append(text)
 
    # 发送命令
    def PostCommand(self, http_node_name, http_node_data={}):
        # self.ConnectToServer()

        # 先检测连接是否断开
        def is_connected( ):
            transport = self.ssh_connect.get_transport() if self.ssh_connect else None
            return transport and transport.is_active()
        if not  is_connected():
            self.ConnectToServer()
        print ("log___is_connected", is_connected())

        project = self.current_project
        masterip   = project["masterip"]
        masterport = project["masterport"]
        if type(http_node_data) == dict:
            http_node_data = json.dumps(http_node_data)
        command = f"curl -X POST 'http://{masterip}:{masterport}{http_node_name}' --data '{http_node_data}'"
        return self.ssh_connect.exec_command(command)

    # 增加命令程按钮
    def AddedALogProcessButton(self, comm, para={}):
        # log_widget = QWidget(self.ui.verticalWidget)
        # log_widget.setObjectName(u"log_widget")
        # log_widget.setMinimumSize(QSize(96, 30))

        log_process = QPushButton(self.ui.scrollAreaWidgetContents)
        log_process.setObjectName(u"log_process")
        # log_process.setGeometry(QRect(0, 0, 96, 30))
        log_process.setMinimumSize(QSize(16777215, 30))
        # log_process.setMaximumSize(QSize(96, 30))
        # log_process.setCheckable(True)
        log_process.setText(
            QCoreApplication.translate("logging_tools", comm, None))
        
        count = len(self.displayed_command_list)
        self.ui.verticalLayout_3.insertWidget(count, log_process)

        log_process.clicked.connect(lambda : self.ClickCommand(count))

        self.displayed_command_list.append({
            "comm": comm,
            "para": para,
            "button": log_process,
        })

        return log_process

    # 创建命令表按钮
    def CreateCommandTableButton(self):
        
        aaa = self.StatisticalSearchResults()
        if aaa != self.displayed_request:
            self.displayed_request = aaa

            for x in self.displayed_command_list:
                x["button"].deleteLater()
            
            self.displayed_command_list = [ ]
            for k,v in self.displayed_request.items():
                self.AddedALogProcessButton(k,v)

    # 点击命令
    def ClickCommand(self, indexes):
        print ("acme_log_click_command", indexes)
        self.OpenCommandDescription(indexes)

    # 字典格式化为html
    def DictionaryFormaDAsHtml(self, data, stratum=1):
        html = "{\n"
        sj = "    "*stratum
        for k,v in data.items():
            if type(v) == str:
                text = '{}<font color="#1DDD30">{}</font>:<font color="#ffffff">{}</font>\n'.format(sj,k,v)
                html += text
            elif type(v) == dict:
                jg = self.DictionaryFormaDAsHtml(v, stratum+1)
                html += '<font color="#555555">{}{}</font>:<font color="#ffffff">{}{}\n</font>'.format(sj,k,jg,sj)
            else:
                html += '<font color="#555555">{}</font>:<font color="#ffffff">{}</font>\n'.format(k,v)
        # text = '<p id=dsfgh>'+text+'</p>'
        return html+"{}}}".format("    "*(stratum-1))

    # 打开命令说明
    def OpenCommandDescription(self, indexes):
        _bool = False
        self.ui.command_handle_scroll_area.setHidden(_bool)  
        self.ui.output_log.setHidden(not _bool) 
        self.selected_command = indexes
        comm = self.displayed_command_list[indexes]["comm"]
        cominfo = self.displayed_request[comm]
        self.ui.command_description.setText("{}\n{}".format(comm, cominfo["desc"]))
        
        param_desc = json.dumps(cominfo.get("paramDesc", "无参数说明"), ensure_ascii=False, indent=4)
        self.ui.parameter_description.setText(param_desc)
        response_desc = json.dumps(cominfo.get("responseDesc", "无返回码说明"), ensure_ascii=False, indent=4)
        # self.ui.response_description.setText(response_desc)
        self.ui.response_description.setText(  #setHtml
            "<pre>"+self.DictionaryFormaDAsHtml(cominfo["responseDesc"])+"</pre>" 
        )
        self.ui.response_description2.setText(  #setHtml
        # response_desc
            "<p>"+self.DictionaryFormaDAsHtml(cominfo["responseDesc"])+"</p>" 
        )
        self.ui.parameter_input.setText(json.dumps(cominfo["defaultParam"], ensure_ascii=False, indent=4))
        # self.ui.command_description.setGeometry() # 参数输入  
        print ("\n")
        self.AdjustCommandDescriptionEditTheBoxSize(20)
        timer=QTimer(self)
        timer.timeout.connect(self.AdjustCommandDescriptionEditTheBoxSize)
        timer.setSingleShot(True)
        timer.start(20)

    # 调整命令说明 编辑框大小
    def AdjustCommandDescriptionEditTheBoxSize(self, add_height = 0):
        height = int(self.ui.command_description.document().size().height())
        self.ui.command_description.setFixedHeight(height + add_height)
        # print (height)
        
        height = int(self.ui.parameter_input.document().size().height())
        self.ui.parameter_input.setFixedHeight(height + add_height)

        height = int(self.ui.parameter_description.document().size().height())
        self.ui.parameter_description.setFixedHeight(height + add_height)


        height = int(self.ui.response_description.document().size().height())
        print (height)
        self.ui.response_description.setFixedHeight(height + add_height)

    # 取消发送命令 
    def CancelSendCommand(self):

        _bool = True
        self.ui.command_handle_scroll_area.setHidden(_bool)  
        self.ui.output_log.setHidden(not _bool) 

    # 发送命令
    def SendCommand(self):
  
        _bool = True
        self.ui.command_handle_scroll_area.setHidden(_bool)  
        self.ui.output_log.setHidden(not _bool) 
        indexes = self.selected_command
        comm = self.displayed_command_list[indexes]["comm"]
        plain_text = self.ui.parameter_input.toPlainText()

        self.ui.output_log.append(comm)
        self.ui.output_log.append(plain_text)

        stdin, stdout, stderr =self.PostCommand(comm, plain_text)
        result = stdout.read().decode()
        result = json.dumps(json.loads(result), ensure_ascii=False, indent=4)
        for x in result.split("\n"):
            x = '<font color="#32C12A"> {}</font>'.format(x)
            self.ui.output_log.append(x)
        # self.ui.output_log.append(result)

    # 搜索字符串
    def SearchString(self, text=""):
        self.search_string = text


    # 滚动条滚动事件
    def ScrollBarScrollEvents(self, height ):
        pass
        # print (height)

    # 统计搜索结果
    def StatisticalSearchResults(self):
        stati_request = { }
        if self.search_string:
            text = self.search_string
            for comm, param in self.all_request.items():
                if [x for x in param["searchKey"] if text in x]:
                    stati_request[comm] = param
                    continue
                elif  text in comm:
                    stati_request[comm] = param
        else:
            stati_request = self.all_request
        return stati_request

    # 搜索字符串
    def SearchString(self, text=""):
        self.search_string = text
        self.CreateCommandTableButton()
        print("搜索字符串", text)

    # 按键按下
    def keyPressEvent(self, QInput):
        print ("key_press_event",  QInput.key() )
        key = QInput.key()
        # print (key, key == QtCore.Qt.Key.Key_Shift)

        # Esc  
        if key == QtCore.Qt.Key_Escape: 
            self.close()

        elif key == QtCore.Qt.Key.Key_C:
            # 热更
            if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
                import overloaded_function
                reload(overloaded_function)
                overloaded_function.Reload(self)

            # c 测试功能按钮
            else:
                import overloaded_function
                reload(overloaded_function)
                overloaded_function.TestFunction(self)


        elif key == QtCore.Qt.Key.Key_F:
            if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier:
                self.ui.lineEdit.setFocus()

        elif key == QtCore.Qt.Key.Key_R:
            "重新链接"

        elif key == QtCore.Qt.Key.Key_Q:
            with open("style_2.qss", "r") as f:
                _style = f.read()
                self.setStyleSheet(_style)
            print ("重载样式表")

    # 按键释放
    def keyReleaseEvent(self, QInput):
        # print ("key_release_event",  QInput.key() )
        pass


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    app.setWindowIcon(QIcon("texture/icons8_minecraft_creeper.ico"))

    asas = '''
        #log_process { padding-left: 8px; text-align:left; background-color: #d4d4d4; border:0px ; }
        #parameter_input { color: #FFFFFF; font-size: 16px; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace,  "宋体",; background-color: #0C0C0C; }
    '''
        #logging_tools{ background-color: #999999; }
        #textEdit { color: #FFFFFF; font-size: 16px; font-family: "宋体", Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; background-color: #0C0C0C; }
        #log_process { padding-left: 8px; text-align:left; background-color: #d4d4d4; border:0px ; }
        #log_process:checked { background-color: #FFFFFF; }
        #log_process:hover { background-color: #b8d7db; }
        #log_process:pressed { background-color: #b8d7db; }
        #log_process:checked { background-color: #b1b1b1; border:2px solid #333333; }
        #close_log_process { background-color:transparent; border:0px ; }
        #close_log_process:hover  { background-color: #b6b6b6; }
        #log_new_number { color: #FFFFFF; font-size: 10px; background-color: #FF0000; border-radius:4px; }
        #title_bar{ background-color: #b6b6b6; }
    app.setStyleSheet(asas)

    try: 
        with open("style.qss", "r") as f:
            _style = f.read()
            app.setStyleSheet(_style)
    except : pass

    widget = MyWidget()
    widget.show()

    sys.exit(app.exec())
